<?

class objDevice {
	
	
	public $id = 0;
	public $description = '';
	public $ip_address = '';
	public $hostname = '';
	public $uptime = '';
	public $os_type = '';
	public $last_discovery = 0;
	public $last_poll = 0;
	public $location = '';
	public $status = 'down';
	public $contactName = '';
	public $contactEmail = '';
	public $poll_freq = 300;
	public $disabled = false;
	public $interfaces = array();	
	public $services = array();	
	public $cpus = array();
	public $storage = array();
	public $memory = array();
	public $type = '';
	
	// SNMP Protocol
	public $useSNMP = false;
	public $snmpCommunity = 'public';
	public $snmpVersion = 'v2c';
	public $os;
	public $snmp;
	
	public function __construct($fields=array()) {
		global $nms;
		$this->snmp = new classSNMP();
		if (is_array($fields) || is_object($fields)) {
			$this->processFields($fields);
		} elseif (is_numeric($fields)) {
			$this->id = (int)$fields;
			if (!empty($this->id)) { $this->load();	}
		}
		
	}
	

	public function load() {
		global $nms;
		$sql = 	"SELECT *,UNIX_TIMESTAMP(last_discovery) AS last_discovery, ".
				"UNIX_TIMESTAMP(last_poll) AS last_poll ".
				"FROM devices ".
				"WHERE id=".$this->id.";";
		$this->processFields($nms->db->getObject($sql));

	}
	protected function processFields($fields) {
		global $nms;
		foreach ($fields AS $name=>$value) {
			switch ($name) {
				case 'snmpCommunity':
				case 'snmpVersion':
				case 'ip_address':
					$this->snmp->$name = $value;
				case 'description':
				case 'hostname':
				case 'type':
				case 'contactName':
				case 'contactEmail':
				case 'location':
				case 'status':
				case 'last_poll':
				case 'last_discovery':
					$this->$name = $value;
					break;
				case 'poll_freq':
				case 'id':
					$this->$name = (int)$value;
					break;
				case 'uptime':
					$this->$name = (float)$value;
					break;
				case 'useSNMP':
				case 'disabled':
					$this->$name = (bool)$value;
					break;
				case 'walk';
					$this->snmp->setWalk(unserialize($value));
					break;		
				case 'os_type':				
					$this->$name = $value;	
					if (!empty($value)) {
						$className = $nms->buildClassName('os_'.$value);
						$this->os = new $className($this);
					}
					break;
			}
		}
	}


	/*********************************************************************************	
		DISCOVER - Discover System Information about Device
	**********************************************************************************/	
	

	public function discover() {
		global $nms;
		$nms->events->add(array(
			'device_id' => $this->id,
			'description' => '##### Starting Device Discovery - '.date('r').' #####'
		));
		// Check we have the host name or ip_address		
		if (empty($this->hostname) && !empty($this->ip_address)) { $this->hostname = gethostbyaddr($this->ip_address);}
		if (empty($this->ip_address) && !empty($this->hostname)) { $this->ip_address = gethostbyname($this->hostname);}		
		$save = array();
		$ping = $this->ping();
		if ($ping > 0) {
			$save['status'] = 'up';			
			if ($this->useSNMP) {
				echo 'Requesting SNMP Information'.chr(10);
				$nms->events->add(array(
					'device_id' => $this->id,
					'description' => 'DISCOVERY: Retrieving SNMP Information'
				));
				$this->snmp = new classSNMP(array(
					'ip_address'=>$this->ip_address,
					'community' => $this->snmpCommunity,
					'version' => $this->snmpVersion
				));	
				if ($this->snmp->walkLoaded()) {
					echo 'Received SNMP Walk Data';
					$save['walk'] = $this->snmp->getWalk();
					$save['last_discovery'] = time();												// Set Last Time Device was Discovered			
					$save['status'] = 'up';														// Mark System as UP		
					$save['uptime'] = $this->os->getUptime();										// Set Device Uptime
					echo 'Uptime: '.$this->uptime.chr(10);		
		
					// Get Device Name
					$sysName = $this->snmp->get('SNMPv2-MIB','sysName.0');
					if (empty($this->description)) { $save['description'] = $sysName;}
					
					// Get Device Description
					$sysDescr = $this->snmp->get('SNMPv2-MIB','sysDescr.0');
					if (empty($this->sysDescr)) { $save['sysDescr'] = $sysDescr;}

					// Get Location
					$this->location = $this->snmp->get('SNMPv2-MIB','sysLocation.0');
					if (preg_match('/^Unknown/',$this->location)) { $this->location = '';}
					
					// Determine OS
					$save['os_type'] = $this->os_type = $nms->determineOS($sysDescr);
					$className = $nms->buildClassName('os_'.$this->os_type);
					if (class_exists($className,true)) {
						$this->os = new $className($this);
					}
					// Get Interfaces
					echo 'Interfaces: '.chr(10);
					$interfaces = $this->snmp->find('IF-MIB','ifIndex');
					foreach ($interfaces AS $interface_id) {
						$params = $this->snmp->find('IF-MIB','if([a-zA-z0-0]+).'.$interface_id,true);
						$params = $params[$interface_id];
						if (isset($this->interfaces[$params[$interface_id]['ifIndex']])) {
							$this->interfaces[$params['ifIndex']]->save($params);
						} else {
							$params['device_id'] = $this->id;
							$this->interfaces[$params['ifIndex']] = new objInterface();				
							$this->interfaces[$params['ifIndex']]->save($params);
						}
						echo '.';
					}
					// Delete Old Interfaces
					foreach ($this->interfaces AS $index=>$interface) {
						if (!$this->snmp->exists('IF-MIB','ifName.'.$index)) {
							//$interface->delete();
							echo 'X';
						}
					}			
				}
			}
		} else {
			$save['status'] = 'down';
		}
		
		if ($this->save($save)) {
			echo 'Device Details Updated'.chr(10);
		} else {
			echo 'Error Updating Device Details'.chr(10);
			echo $nms->db->lastError;
		}
	}
	
	
	/*********************************************************************************	
		SAVE - Save Changes back to the database
	**********************************************************************************/	
	
	public function save($params=array()) {
		global $nms;
		if (!empty($this->id)) {
			// Update Existing Device
			$fields = array();
			$logs = array();
			foreach ($params AS $name=>$value) {
				switch ($name) {
					case 'walk':
						$fields[] = "walk='".$nms->db->escape(serialize($value))."'";
						break;
					case 'hostname':
					case 'ip_address':
					case 'status':
					case 'location':
					case 'os_type':
						$fields[] = $name."='".$nms->db->escape($value)."'";
						break;
					case 'uptime':
						$fields[] = $name."=".(float)$value;
						break;
					case 'last_discovery':
					case 'last_poll':
						$fields[] = $name."='".date('Y-m-d G:i:s',$value)."'";
						break;

				}
			}
			
			
			
			if (!empty($fields)) {
				$sql = "UPDATE devices SET ".implode(',',$fields)." WHERE id=".$this->id.";";
				//echo $sql;
				if ($nms->db->query($sql)) {
					return true;				
				} else {
					echo 'SQL:'.$sql.chr(10);
					return false;
				}			
			} else return false;					
		} else {
			$fields = array();
			$values = array();
			foreach ($this AS $name=>$value) {
				switch ($name) {
					case 'walk':
						$fields[] = "walk";
						$values[] = "'".$nms->db->escape(serialize($value))."'";
						break;
					case 'snmpCommunity':
					case 'snmpVersion':
					case 'hostname':
					case 'ip_address':
					case 'status':
					case 'location':
					case 'os_type':
						$fields[] = $name;
						$values[] = "'".$nms->db->escape($value)."'";
						break;
					case 'uptime':
						$fields[] = $name;
						$values[] = (float)$value;
						break;
					case 'last_discovery':
					case 'last_poll':
						$fields[] = $name;
						$values[] = "'".date('Y-m-d G:i:s',$value)."'";
						break;
				}
			}
		
		
		
		
		
		
		
		}
	}
	
	/*********************************************************************************	
		POLL - Schedule Task run every 5 Minutes to get update Devices statistics
	**********************************************************************************/	

	public function poll() {
		global $nms;
		$nms->events->add(array(
			'device_id' => $this->id,
			'description' => '##### Starting Device Poll - '.date('r').' #####'
		));
		echo '*****************************************************************************'.chr(10);
		echo 'Checking Device ('.$this->id.') Details: '.$this->getHostName().chr(10);
		echo '*****************************************************************************'.chr(10);
		echo 'Poll Freq: '.$this->poll_freq.chr(10);
		$save= array();
		$ping = $this->ping();
		if ($ping > 0) {
			$save['status'] = 'up';
			if ($this->useSNMP == true) {
				$mibs = array();
				if (isset($this->os->mibs) && is_array($this->os->mibs)) {
					foreach ($this->os->mibs AS $filename) {
						$mibs[] = $nms->getPath('mib').$filename;
					}
				}
				$this->snmp = new classSNMP(array(
					'ip_address'=>$this->ip_address,
					'community' => $this->snmpCommunity,
					'version' => $this->snmpVersion
					//,'mibs' => $mibs
				));	
				if (isset($this->os->snmpSections) && !empty($this->os->snmpSections)) {
					$this->snmp->getData(implode(' ',$this->os->snmpSections));
				}
				if ($this->snmp->walkLoaded() == true) {
					$this->os->getResources();
					$save['walk'] = $this->snmp->getWalk();
					$save['last_polled'] = time();
					$save['uptime'] = $this->os->getUptime();
					// Process Network Interfaces
					foreach ($this->interfaces AS $interface) {
						if (!$interface->ignore) {
							$params = $this->snmp->find('IF-MIB','if([a-zA-z0-0]+).'.$interface->ifIndex,true);
							$params = $params[$interface->ifIndex];
							$interface->setrrd($this->getRrdPath(),$params,$this->poll_freq);
							$interface->processFields($params);
							if ($interface->save()) {
								echo 'Interface: '.$interface->getDescription().' Successfully Updated'.chr(10);
							} else {
								echo 'Interface: '.$interface->getDescription().' Error Occured'.chr(10);			
								echo 'SQL Error: '.$nms->db->lastError.chr(10);
							}
						} else {
							if ($interface->save()) {
								echo 'Interface: '.$interface->getDescription().' Successfully Updated'.chr(10);
							} else {
								echo 'Interface: '.$interface->getDescription().' Error Occured'.chr(10);			
								echo 'SQL Error: '.$nms->db->lastError.chr(10);
							}
						}
					}
					if (isset($this->os->dataCollectors) && !empty($this->os->dataCollectors)) {
						foreach ($this->os->dataCollectors AS $method) {
							if (method_exists($this->os,$method)) { $this->os->$method();}
						}
					}
				}
			}
			// Process Device Services
			$this->loadServices();
			foreach ($this->services AS $service) {
				if (!$service->ignore) {			
					$service->poll();				
				}					
			}

		} else {
			if ($this->status != 'down') {
				$nms->events->add(array(
					'device_id' => $this->id,
					'description' => $this->getDescription().' unresponsive',
					'status' => 'open',
					'type' => 'alert',
					'level' => 'critical'				
				));			
			}
			echo 'Device Down!!!!!'.chr(10);
			$save['status'] = 'down';		
		}
		if (sizeof($save)>0) {
	 		if ($this->save($save)) {
				echo 'Device Details Updated'.chr(10);
			} else {
				echo 'Error Updating Device Details: '.$nms->db->lastError.chr(10);
			}
		} else {
			echo 'No Details to Update'.chr(10);
		}
	
	}

	public function findInterface($string='') {
		foreach ($this->interfaces AS $interface) {
			if (preg_match ('/'.$string.'/',$interface->ifDescr)) {
				return $interface;
			}
		}	
	}

	public function isUp() { return $this->status=='up'?true:false;}
	public function getHostName() { return !empty($this->hostname)?$this->hostname:$this->ip_address;}
	public function getDescription() {return empty($this->description)?$this->os->getDescription():$this->description;}
	public function getUpTime($format='') {	global $nms; return $nms->format_uptime($this->uptime);}	
	public function loadInterfaces() {
		global $nms;
//		$this->interfaces = array();
		$sql = "SELECT * FROM interfaces WHERE device_id=".$this->id." ORDER BY ifIndex ASC;";
		foreach ($nms->db->getObjects($sql) AS $data) {
			if (isset($this->interfaces[$data->ifIndex])) {
				$this->interfaces[$data->ifIndex]->update($data);
			} else {

			}
		}	
	}
	
	public function getRrdPath() {
		global $nms;
		$path = $nms->getPath('rrd').$this->id.'/';		
		if (!is_dir($path)) { mkdir($path,0777,true);}		
		return $path;
	}
	/*********************************************************************************	
		PING - Check if the device is up and responding
	**********************************************************************************/	
	public function ping($count=3,$timeout=5,$store=true) {
		
		$cmd = 'ping -c '.$count.' -w '.$timeout.' '.escapeshellcmd($this->ip_address);
		$results = array();
		exec($cmd, $results);
		if (!is_array($results) || count($results) == 0) {
			if ($store) $this->storePingRrd(-1);
			return 0;
		} else {
			$result = implode(chr(10),$results);
			if (preg_match_all('/time=([0-9\.]+)\sms/i', $result, $matches)) {        	
				$value = round(array_sum($matches[1])/count($matches[1]),3);
				if ($store) $this->storePingRrd($value);
				return $value;
			} else {
				if ($store) $this->storePingRrd(-1);
				return 0;
			}
		}	
	}
	
	/*********************************************************************************	
		STORE PING into RRD File - Store the Ping Response time for later graphing
	**********************************************************************************/	
	private function storePingRrd($value=0) {
		global $nms;
		// RRD Data File
		$path = $this->getRrdPath().'ping.rrd';
		// Create new Data file is one does not already exist
		if (!is_file($path)) {
			$opts = array(
				'--step',$this->poll_freq,
				'--start','0',
				'DS:PINGTIME:GAUGE:600:U:U',
				'RRA:AVERAGE:0.5:1:1200',
				'RRA:MAX:0.5:1:1200'
			);
			if (rrd_create($path, $opts, count($opts)) == 0) { 
				$nms->events->log(array(
					'device_id' => $this->id,
					'type' => 'error',
					'description' => 'Unable to create Ping Response Time Data File',
					'notes' => rrd_error(),
					'open' => false,
					'level' => 'critical'
				));
				return false;
			}
		}		
		$rrd = array();
		$rrd[] = 'N';
		$rrd[] = $value/1000;
		if (rrd_update($path,implode(':',$rrd)) == 0) {
			echo rrd_error();
			return false;
		} else {
		
			return true;
		}
	}
	public function getGraphPath($params=array()) {return $this->generateGraphPath($params);}
	public function generateGraphPath($params=array()) {
		global $nms;
		return $nms->getPath('cache').strtolower('graph_device_'.$this->id.'_'.$params['type'].'_'.$params['start'].'_'.$params['width'].'x'.$params['height'].'.png');	
	}
	
	public function graph($params= array()) {
		$path = $this->getRrdPath();
		$className = 'graph'.ucfirst($params['type']);
		$graph = new $className($this,$params);
		$graph->generate($this->generateGraphPath($params));
	}
	
	
		
	public function loadServices() {
		global $nms;
		$sql = "SELECT * FROM services WHERE device_id=".$this->id.";";
		$services = $nms->db->getObjects($sql);
		if (!empty($services)) {
			foreach ($services AS $service) {
				$class = $nms->buildClassName('service_'.$service->type);				
				if (class_exists($class,true)) {
					$this->services[$service->id] = new $class($service);			
				} else {
					$this->services[$service->id] = new serviceGeneric($service);			
				}
			}
		}	
	}
	
	public function determineIP() {
		if (empty($this->hostname) && !empty($this->ip_address)) { $this->hostname = gethostbyaddr($this->ip_address);}
		if (empty($this->ip_address) && !empty($this->hostname)) { $this->ip_address = gethostbyname($this->hostname);}
		if (empty($this->ip_address))  return false; else return $this->ip_address;
	} 
	
	
	public function checkSNMP() {
		$this->determineIP();
		$this->snmp = new classSNMP(array(
			'ip_address'=>$this->ip_address,
			'community' => $this->snmpCommunity,
			'version' => $this->snmpVersion
			//,'mibs' => $mibs
		));	
		if ($this->snmp->getData('SNMPv2-MIB::sysName.0')) {
			if ($this->snmp->exists('SNMPv2-MIB','sysName.0')){
				return $this->snmp->get('SNMPv2-MIB','sysName.0');
			} else return false;
		} else return false;
	}
}